package org.example.seckill.controller;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import org.example.seckill.bean.*;
import org.example.seckill.rabbitmq.seckill.SeckillMsgSender;
import org.example.seckill.service.*;
import org.example.seckill.util.timeUtil;
import org.example.seckill.vo.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author woopsion
 * @date 2025-07-01
 */
@RestController
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {
    
    @Autowired
    private orderService orderService;
    @Autowired
    private seckillOrderService sgo;
    @Autowired
    private goodsService goodsService;
    @Autowired
    private seckillGoodsService sgs;
    @Autowired
    private SeckillMsgSender seckillMsgSender;
    
    /**
     * IS_HAVE_STOCK 内存标记优化
     * true 表示还有库存
     * false 表示没有库存了
     */
    private final HashMap<Long,Boolean> IS_HAVE_STOCK = new HashMap<>();
   
    @Resource
    private RedisTemplate redisTemplate;
    
    @Autowired
    private RedisWithGoodsInfoService redisWithGoodsInfoService;
    
    @RequestMapping("/seckill/detail/{goodsId}")
    public RespBean getseckilldetail(@PathVariable("goodsId") Long goodsId,
                                     User user){
        //鉴定权限
        if (user == null){return RespBean.error(RespBeanEnum.AUTHENTICATION_ERROR);}
        
        SeckillStatus status= sgs.getSeckillStatusByGoodsId(goodsId);
        
        if (status.getStatus() == -1){return RespBean.error(RespBeanEnum.Error,status);}
        
        goods gs = redisWithGoodsInfoService.getGoods(goodsId);
        seckillGoods gs_kill = redisWithGoodsInfoService.getseckillGoods(goodsId);
        
        Map<String,Object> retrurnMap = new HashMap<>();
        retrurnMap.put("seckillStatus",status);
        if(status.getStatus()==1){
            //未开始
            Long toStartTime = timeUtil.DateOperator(new Date(), gs_kill.getStartDate());
            retrurnMap.put("toStartTime",toStartTime);
        }else {retrurnMap.put("toStartTime",-1);}
        if (status.getStatus() == 2 ){ 
            // 进行中           
            Long toEndTime = timeUtil.DateOperator(new Date(), gs_kill.getEndDate()); 
            retrurnMap.put("toEndTime",toEndTime);}
        else {retrurnMap.put("toEndTime",-1);}
        
        retrurnMap.put("goodsTitle",gs.getGoodsTitle());
        retrurnMap.put("goodsName",gs.getGoodsName());
        retrurnMap.put("goodsKillPrice",gs_kill.getSeckillPrice());
        retrurnMap.put("goodsStartTime",gs_kill.getStartDate());
        retrurnMap.put("goodsEndTime",gs_kill.getEndDate());
        return RespBean.success(retrurnMap);
    }
    
    
    
    @Deprecated
    @RequestMapping("/seckill/{goodsId}")
    public RespBean doSeckill(User user, @PathVariable("goodsId") Long goodsId ){
        
        
        //用户权限认证
        if (user == null){return RespBean.error(RespBeanEnum.LOGIN_ERROR); }
        
        
        //查看商品的状态，即 开卖？ 未开始？  进行中？  结束 
//        SeckillStatus seckillStatusByGoodsId = sgs.getSeckillStatusByGoodsId(goodsId);
        //不是秒杀活动中
//        if (seckillStatusByGoodsId.getStatus() != 2){ return RespBean.error(RespBeanEnum.Error,seckillStatusByGoodsId);}
        
        
        //利用redis进行缓存查询秒杀订单  判断用户是否 抢购过该商品
        Object seckill_order= redisTemplate.opsForValue().get("order:seckill:+" + user.getId() + ":" + goodsId);
        if(seckill_order != null) return RespBean.error(orderStatus.ORDER_ERROR_HAVE_SECKILL_ORDER);
        
        
        //现在内存中检查一下库存的标记(判断该商品是否还具有库存)  优化加速
        if(!IS_HAVE_STOCK.get(goodsId)) return RespBean.error(SeckillStatus.SALE_GOODS_OUT);
        
        
        //预见redis中商品数量，decrement是原子性的操作
        Long decrement = redisTemplate.opsForValue().decrement("seckill:goods:stock:" + goodsId);
        if (decrement <0){
            redisTemplate.opsForValue().increment("seckill:goods:stock:" + goodsId);//将库存恢复为0
            IS_HAVE_STOCK.put(goodsId,false);//没库存了
            return RespBean.error(SeckillStatus.SALE_GOODS_OUT);
        }

        //查询商品信息，以及秒杀商品信息 并发涌入问题
        goods goods = goodsService.getById(goodsId);
        seckillGoods seckillGoodsInfo = sgs.getById(goodsId);

//        //库存不足订单创建失败  （废弃 内存 + redis 双检查 优化查询）
//        if (goods.getGoodsStock()<1){return RespBean.error(RespBeanEnum.LOGIN_ERROR); }
        
// 如果该用户已经买过该商品 拒绝购买申请
//        seckillOrder seckillOrder = sgo.getOne(new QueryWrapper<seckillOrder>().eq("user_id", user.getId()
//                        ).eq("goods_id", goodsId));
//        if(seckillOrder != null){ return RespBean.error(RespBeanEnum.Error, orderStatus.ORDER_ERROR_NO_RIGHT); }

        //完成秒杀订单创建
        orderStatus orderStatus = sgo.doSeckill(user, goods, seckillGoodsInfo);
        
        
        return RespBean.success(orderStatus);
    }


    /**
     * 加入消息队列优化秒杀结构
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping("/seckill_MQ/{goodsId}")
    public RespBean doSeckill_MQ(User user, @PathVariable("goodsId") Long goodsId ){
        //权限认证
        if (user == null){return RespBean.error(RespBeanEnum.LOGIN_ERROR); }
        
        //利用redis进行缓存查询秒杀订单  判断用户是否 抢购过该商品
        Object seckill_order= redisTemplate.opsForValue().get("order:seckill:+" + user.getId() + ":" + goodsId);
        if(seckill_order != null) return RespBean.error(orderStatus.ORDER_ERROR_HAVE_SECKILL_ORDER);
        
        //现在内存中检查一下库存的标记(判断该商品是否还具有库存)  优化加速
        if(!IS_HAVE_STOCK.get(goodsId)) return RespBean.error(SeckillStatus.SALE_GOODS_OUT);
        
        //预见redis中商品数量，decrement是原子性的操作
        Long decrement = redisTemplate.opsForValue().decrement("seckill:goods:stock:" + goodsId);
        if (decrement <0){
            redisTemplate.opsForValue().increment("seckill:goods:stock:" + goodsId);//将库存恢复为0
            IS_HAVE_STOCK.put(goodsId,false);//没库存了
            return RespBean.error(SeckillStatus.SALE_GOODS_OUT);
        }
        
        //查询商品信息，以及秒杀商品信息 并发涌入问题
        goods goods = redisWithGoodsInfoService.getGoods(goodsId);
        seckillGoods seckillGoodsInfo = redisWithGoodsInfoService.getseckillGoods(goodsId);
        seckillMsgSender.send_killMsg(new SeckillMsg(user,goods,seckillGoodsInfo));
        return RespBean.success("秒杀进行中...");
        
    }
    
    
    @RequestMapping("/orderStatus/{goodsId}")
    public RespBean getSeckillOrderStatus(User user,
                                          @PathVariable("goodsId") Long goodsId){
        if (user == null) return RespBean.error(RespBeanEnum.LOGIN_ERROR);
        seckillOrder o = (seckillOrder)redisTemplate.opsForValue().get("order:seckill:+" + user.getId() + ":" + goodsId);
        if (o == null) return RespBean.error(orderStatus.ORDER_ERROR_NO_INFO);
        Object o1 = redisTemplate.opsForValue().get("order:detail:+" + user.getId() + ":" + goodsId);
        return RespBean.success(o1);
    }
    
    
    /**
     * bean的初始化方法 进行秒杀商品的预加载
     * 1.未进行判断，即秒杀商品是否是开启秒杀的，即未做时间上的处理
     * 2.仅仅是将秒杀商品的详情加载到了redis中
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<seckillGoods> list = sgs.list();
        if(CollectionUtils.isEmpty(list)) return;//空就返回
        list.forEach(goods ->{
            
            //秒杀商品 id 以及库存信息的预加载  所有的秒杀商品
            redisTemplate.opsForValue().set("seckill:goods:stock:"+goods.getId(),goods.getStockCount());
            IS_HAVE_STOCK.put(goods.getId(),true);
        });
        
    }
}
